home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / src / vmake / File.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-09  |  20.3 KB  |  726 lines

  1. /*
  2.  *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
  3.  *    use is allowed under the terms of the DICE-LICENSE FILE,
  4.  *    DICE-LICENSE.TXT.
  5.  */
  6. #include "vmake.h"
  7.  
  8. Prototype int get_filename(char *str, char *buf, int savemode);
  9. Prototype void commit_filename(char *buf);
  10. Prototype void read_script(void);
  11. Prototype int read_file(void);
  12. Prototype int write_file(void);
  13. Prototype void expand_filename(char *name, char *fullname);
  14. Prototype void go_dir(BPTR dirlock);
  15. Prototype BPTR xlockdir(char *path);
  16.  
  17. #define LINE_LEN  78
  18. #define MIN_LINE  10
  19.  
  20. static BPTR XSaveLock;
  21. static BOOL XSaveLockValid;
  22.  
  23. /***********************************************************************************
  24.  * Procedure: restorecurdir
  25.  * Synopsis:  (void)restorecurdir();  - Called at exit time if necessary
  26.  * Purpose:   Restores the original directory from where the program started
  27.  ***********************************************************************************/
  28. static void restorecurdir()
  29. {
  30.    if (XSaveLockValid)
  31.       UnLock(CurrentDir(XSaveLock));
  32.    XSaveLockValid = 0;
  33. }
  34.  
  35. /***********************************************************************************
  36.  * Procedure: go_dir
  37.  * Synopsis:  (void)go_dir(dirlock);
  38.  * Purpose:   Set the current directory to one specified by the given lock
  39.  ***********************************************************************************/
  40. void go_dir(BPTR dirlock)
  41. {
  42.    BPTR lock;
  43.  
  44.    if (dirlock)
  45.    {
  46.       if ( (lock = DupLock(dirlock)) != NULL)
  47.       {
  48.          if (!XSaveLockValid)
  49.          {
  50.             XSaveLock = CurrentDir(lock);
  51.             XSaveLockValid = 1;
  52.             atexit(restorecurdir);
  53.          }
  54.          else
  55.          {
  56.             UnLock(CurrentDir(lock));
  57.          }
  58.       }
  59.    }
  60. }
  61.  
  62. BPTR xlockdir(char *path)
  63. {
  64.    BPTR lock;
  65.  
  66.    lock = Lock(path, SHARED_LOCK);
  67.  
  68.    /* We should check to make sure that the lock is valid */
  69.    return(lock);
  70. }
  71.  
  72. /***********************************************************************************
  73.  * Procedure: get_filename
  74.  * Synopsis:  type = get_filename(str)
  75.  * Purpose:   Parse out a file descriptor and return a type for that name
  76.  *            This routine does not modify any permanent global data
  77.  ***********************************************************************************/
  78. int get_filename(char *str, char *buf, int savemode)
  79. {
  80.    char *p;
  81.    int len;
  82.  
  83.    go_dir(global.homedir);
  84.  
  85.    if (*str)
  86.    {
  87.       /* They actually specified a name.  All we need to do is replace the */
  88.       /* current one.  If they specified a '?', then we need to prompt the */
  89.       /* user with the file requester.  We try to use ASL first and then   */
  90.       /* fall back to ARP.                                                 */
  91.       if (*str == '?')
  92.       {
  93.          int n;
  94.  
  95.          if (global.inrexx)
  96.          /* don't put up requesters if called from rexx - just fail */
  97.          {
  98.             global.rexxrc = TEXT_BADPROJ;  /* sort of fits the bill... */
  99.             return 0;
  100.          }
  101.  
  102.          if (global.freq == NULL) return(0);
  103.  
  104.          if (AslBase != NULL)
  105.          {
  106.             struct TagItem taglist[10];
  107.  
  108.             n = 0;
  109.             taglist[n  ].ti_Tag  = ASL_Pattern;
  110.             taglist[n++].ti_Data = (ULONG)global.text[CONFIG_PATTERN];
  111.  
  112.             taglist[n  ].ti_Tag  = ASL_File;
  113.             taglist[n++].ti_Data = (ULONG)"";
  114.  
  115.             taglist[n  ].ti_Tag  = ASL_Dir;
  116.             taglist[n++].ti_Data = (ULONG)"";
  117.  
  118.             taglist[n  ].ti_Tag  = ASL_Window;
  119.             taglist[n++].ti_Data = (ULONG)global.window;
  120.  
  121. #ifdef ASLFR_DoSaveMode
  122.             taglist[n  ].ti_Tag  = ASLFR_DoSaveMode;
  123.             taglist[n++].ti_Data = savemode;
  124. #endif
  125.  
  126. #ifdef ASLFR_DoPatterns
  127.             taglist[n  ].ti_Tag  = ASLFR_DoPatterns;
  128.             taglist[n++].ti_Data = TRUE;
  129. #endif
  130.  
  131. #ifdef ASLFR_RejectIcons
  132.             taglist[n  ].ti_Tag  = ASLFR_RejectIcons;
  133.             taglist[n++].ti_Data = TRUE;
  134. #endif
  135.  
  136. #ifdef ASLFR_SleepWindow
  137.             taglist[n  ].ti_Tag  = ASLFR_SleepWindow;
  138.             taglist[n++].ti_Data = TRUE;
  139. #endif
  140.  
  141.             taglist[n  ].ti_Tag  = TAG_DONE;
  142.             taglist[n++].ti_Data = 0;
  143.  
  144.             if (!AslRequest( (APTR)global.freq, taglist))
  145.                return(0);
  146.  
  147.          }
  148.          else
  149.          {
  150.             /* We must have arp.library (otherwise we wouldn't have a file     */
  151.             /* Requester structure.)                                           */
  152.             if (!ArpFileRequest( global.freq ))
  153.                return(0);
  154.          }
  155.  
  156.          /* Since the ASL requester and the ARP requester have the pointers to */
  157.          /* the directory and the filename in the same place, we can use the   */
  158.          /* same code to parse both of them                                    */
  159.          n = strlen(global.freq->rf_Dir);
  160.          len = strlen(global.freq->rf_File);
  161.          if (len == 0)
  162.             return (0);  /* didn't pick a name */
  163.          if (n > MAX_FILENAME) n = MAX_FILENAME;
  164.          p = buf;
  165.          memcpy(p, global.freq->rf_Dir, n);
  166.          p += n;
  167.  
  168.          if ((n > 0) && (p[-1] != ':'))
  169.          {
  170.             *p++ = '/';
  171.             n++;
  172.          }
  173.          if ((n + len) > MAX_FILENAME) len = MAX_FILENAME-n;
  174.          strncpy(p, global.freq->rf_File, len);
  175.          p[len] = 0;
  176.       }
  177.       else
  178.       {
  179.          /* Copy over the file name.  Stop when we get to a '?' */
  180.          int len;
  181.          p = strchr(str, ';');
  182.          if (p)
  183.             len = p-str;
  184.          else
  185.             len = strlen(str);
  186.          if (len > MAX_FILENAME) len = MAX_FILENAME;
  187.          memcpy(buf, str, len);
  188.          buf[len] = 0;
  189.       }
  190.    }
  191.    else
  192.    {
  193.       /* default is to use existing global.filename */
  194.       strcpy(buf, global.filename);
  195.    }
  196.    return(1);
  197. }
  198.  
  199. /***********************************************************************************
  200.  * Procedure: commit_filename
  201.  * Synopsis:  void get_filename(str)
  202.  * Purpose:   Set the global data to use a new file name (from get_filename())
  203.  ***********************************************************************************/
  204. void commit_filename(char *buf)
  205. {
  206.    char *p;
  207.  
  208.    /* Given a filename, we want to now set a current directory for the         */
  209.    /* Project.  This means that we need to split the name into a directory     */
  210.    /* and file portion.  We should also set the current directory.  Note that  */
  211.    /* It might be the case that the dmakefile has a directory override for the */
  212.    /* sources.  This will work out fine because we will use the full path for  */
  213.    /* the dmakefile.                                                           */
  214.    /* We want to set the symbols DIR and DMAKEFILE                             */
  215.    /* Start by saving the temporary copy of the new filename.                  */
  216.    strcpy(global.filename, buf);
  217.    p = strrchr(global.filename, '/');
  218.  
  219.    if (p == NULL) p = strrchr(global.filename, ':');
  220.    if (p != NULL)
  221.    {
  222.       char *tp;
  223.       int c;
  224.       BPTR dirlock;
  225.  
  226.       tp = p;
  227.       /* we need to include the colon in a root directory name, then    */
  228.       /* null terminate so we can get a lock on the project directory   */
  229.       if (*tp == ':') tp += 1;
  230.       c = *tp;
  231.       *tp = 0;
  232.       dirlock = xlockdir(global.filename);
  233.       if (dirlock)
  234.       {
  235.          UnLock(global.homedir);
  236.          global.homedir = dirlock;
  237.       }
  238.  
  239.       *tp = c;
  240.       Sym_Set(SYM_SCRIPT, p+1, NULL);
  241.    }
  242.    else
  243.    {
  244.       BPTR dirlock;
  245.       dirlock = xlockdir("");
  246.       if (dirlock)
  247.       {
  248.          UnLock(global.homedir);
  249.          global.homedir = dirlock;
  250.       }
  251.       Sym_Set(SYM_SCRIPT, global.filename, NULL);
  252.    }
  253.  
  254.    /* Now we want to update the title bar */
  255.    if (*global.filename)
  256.    {
  257.       int len;
  258.       strcpy(global.title, global.text[TEXT_PROJECT]);
  259.       len = strlen(global.title);
  260.       strncpy(global.title+len, Sym_Lookup(SYM_SCRIPT), MAX_TITLE-len);
  261.       global.title[MAX_TITLE-1] = 0;
  262.    }
  263.    else
  264.    {
  265.       strcpy(global.title, global.text[TEXT_NOPROJ]);
  266.    }
  267.  
  268.    SetWindowTitles(global.window, global.title, global.title2);
  269. }
  270.  
  271.  
  272. //***********************************************************************
  273. //* Procedure: expand_filename
  274. //* Synopsis:  expand_pathname(name)
  275. //* Purpose:   Constructs a fully expanded filename
  276. //*            Note, we can not assume that the file exists, so it will
  277. //*            not be possible to actually lock it.  We can assume that
  278. //*            the directory it is part of does exist.  It will just
  279. //*            return the name if the expansion fails in any way.
  280. //* Assumptions:  Buffer for fullname is at least large enough to hold
  281. //*            name, and at least MAX_FILENAME+1 bytes.  This routine 
  282. //*            will not return more than MAX_FILENAME bytes.
  283. //***********************************************************************
  284. void expand_filename(char *name, char *fullname)
  285. {
  286.    BPTR lock;
  287.    __aligned struct FileInfoBlock fib;
  288.    char *tail, *p;
  289.    int pos;
  290.    char buf[MAX_FILENAME+1];
  291.  
  292.    //
  293.    // Step 1 - split out any directory information from the actual name
  294.    //
  295.    p = strrchr(name, '/');
  296.    if (p == NULL) p = strrchr(name, ':');
  297.    if (p != NULL)
  298.    {
  299.       //
  300.       // There was some directory information involved
  301.       //
  302.       char c;
  303.       tail = p+1;
  304.       c = p[1];
  305.       p[1] = 0;
  306.       lock = Lock(name, SHARED_LOCK);
  307.       p[1] = c;
  308.    }
  309.    else
  310.    {
  311.       //
  312.       // No directory information involved, just the name relative to the
  313.       // current directory
  314.       //
  315.       lock = Lock("", SHARED_LOCK);
  316.       tail = name;
  317.    }
  318.  
  319.    //
  320.    // Step 2 - we have the lock on the directory and the tail part of the name
  321.    // We want to construct a fully qualified path for the directory.
  322.    // If for some reason the lock on the directory returned 0, we want to just
  323.    // return the name they gave us to begin with.
  324.    //
  325.    if (lock == 0)
  326.    {
  327.       strcpy(fullname, name);
  328.       return;
  329.    }
  330.  
  331.    //
  332.    // Step 3 - Fully qualify the directory portion into the buffer
  333.    //
  334.    if (DOSBase->dl_lib.lib_Version >= 36)
  335.    {
  336.       if (!NameFromLock(lock, buf, MAX_FILENAME))
  337.       {
  338.          //
  339.          // Either the name is too long or there was something else wrong with
  340.          // the file name, just return what they gave us as a start
  341.          //
  342.          UnLock(lock);
  343.          strcpy(fullname, name);
  344.          return;
  345.       }
  346.       UnLock(lock);
  347.       pos = 0;
  348.    }
  349.    else
  350.    {
  351.       // Running under 1.3, we have to do this the old fashion way
  352.  
  353.       //
  354.       // Just so we don't have to do any inserts/extra copies, we will work
  355.       // from the end of the buffer and insert as we go
  356.       //
  357.       pos = MAX_FILENAME-1;  // Leave room for a '/' on the end sometimes
  358.       buf[--pos] = 0;
  359.       while(lock != 0)
  360.       {
  361.          BPTR parent;
  362.          int len;
  363.  
  364.          //
  365.          // Examine the lock to get the name for it
  366.          //
  367.          Examine(lock, &fib);
  368.  
  369.          //
  370.          // Find the parent of this directory
  371.          //
  372.          parent = ParentDir(lock);
  373.          UnLock(lock);
  374.          lock = parent;
  375.  
  376.          len = strlen(fib.fib_FileName);
  377.          pos -= 1;
  378.  
  379.          if (len > pos)
  380.          {
  381.             //
  382.             // oops, not enough room, just return the name they gave us
  383.             //
  384.             UnLock(lock);
  385.             strcpy(fullname, name);
  386.             return;
  387.          }
  388.          buf[pos] = lock ? '/' : ':';
  389.          pos -= len;
  390.          memcpy(buf+pos, fib.fib_FileName, len);
  391.       }
  392.    }
  393.  
  394.    //
  395.    // We have the path part in the buffer and the name part in the tail
  396.    // All that is left is to concatenate them together correctly
  397.    //
  398.    {
  399.       int len;
  400.  
  401.       //
  402.       // Successful, the buf holds the path for the directory.  We will need
  403.       // to add a / to the end if it doesn't end in a colon
  404.       //
  405.       len = strlen(buf+pos);
  406.       if ((buf[pos+len-1] != ':') && (buf[pos+len-1] != '/'))
  407.       {
  408.          buf[pos+len++] = '/';
  409.          buf[pos+len] = 0;
  410.       }
  411.  
  412.       if ((len + strlen(tail)) >= MAX_FILENAME)
  413.       {
  414.          //  Oops, it's to long, just use the name
  415.          strcpy(fullname, name);
  416.       }
  417.       else
  418.       {
  419.          //  Tack the name onto the path
  420.          strcpy(fullname, buf+pos);
  421.          strcpy(fullname+len, tail);
  422.       }
  423.    }
  424.    return;
  425. }
  426.  
  427.  
  428. /***********************************************************************************
  429.  * Procedure: read_script
  430.  * Synopsis:  read_script()
  431.  * Purpose:   Read in a config script
  432.  ***********************************************************************************/
  433. void read_script()
  434. {
  435.    char buf[256];
  436.    FILE *fp;
  437.    char *val;
  438.    int len;
  439.  
  440.    go_dir(global.homedir);
  441.  
  442.    val = Sym_Lookup("TYPE");
  443.    if (!*val) val = "Normal";
  444.  
  445.    if ((!strchr(val, '/')) && (!strchr(val, ':')))
  446.    {
  447.       sprintf(buf, "DCC:CONFIG/%s.DMakefile", val);
  448.       val = buf;
  449.    }
  450.  
  451.    fp = fopen(val, "r");
  452.    if (fp == NULL)
  453.    {
  454.       request(1, TEXT_BADFILE, val, NULL);
  455.       return;
  456.    }
  457.  
  458.    Sym_Set(SYM_HOLD, "", NULL);
  459.    while((len = fread(buf, 1, 255, fp)) > 0)
  460.    {
  461.       buf[len] = 0;
  462.       Sym_Set(SYM_HOLD, NULL, buf);
  463.    }
  464.    fclose(fp);
  465. }
  466.  
  467. /***********************************************************************************
  468.  * Procedure: read_file
  469.  * Synopsis:  rc = read_file()
  470.  * Purpose:   Read in a dmakefile
  471.  ***********************************************************************************/
  472. int read_file()
  473. {
  474.    char buf[256];
  475.  
  476.    FILE *fp;
  477.    int cstate, len;
  478.    char name[64];
  479.    char *fname;
  480.  
  481.    fname = Sym_Lookup(SYM_SCRIPT);
  482.  
  483.    go_dir(global.homedir);
  484.  
  485.    fp = fopen(fname, "r");
  486.    if (fp == NULL)
  487.    {
  488.       request(1, TEXT_BADFILE, fname, NULL);
  489.       return(1);
  490.    }
  491.    cstate = 1;
  492.    while(cstate && (fgets(buf, 256, fp) != NULL))
  493.    {
  494.       int len;
  495.       int extend;
  496.       char *p, *val;
  497.  
  498.       len = strlen(buf)-1;
  499.  
  500.       /* Skip any blank lines */
  501.       if (len < 2) continue;
  502.  
  503.       /* Ignore any comment lines (note that a # at the start of a line where the */
  504.       /* previous line had a continuation mark is not considered a comment)       */
  505.       if (buf[0] == '#' && cstate == 1)
  506.       {
  507.          /* When we get to a line that says:                                      */
  508.          /*    #### AUTOMATICALLY GENERATED - DO NOT EDIT BELOW THIS LINE         */
  509.          /* we want to just exit the loop and not gather any more lines           */
  510.          if (!memcmp(buf, "#### AUTOMATICALLY GENERATED", 28)) break;
  511.          continue;
  512.       }
  513.  
  514.       extend = 0;
  515.       buf[len] = 0; /* Wipe out the \n */
  516.       if (buf[len-1] == '\\')
  517.       {
  518.          extend = 1;
  519.          buf[--len] = 0;
  520.       }
  521.  
  522.       if (cstate == 2)
  523.       {
  524.          /* This was a continuation line, just append it to the */
  525.          /* Current string.                                     */
  526.          Sym_Set(name, NULL, buf);
  527.       }
  528.       else
  529.       {
  530.          /* We need to parse out the name */
  531.          p = strchr(buf, '=');
  532.          if (p == NULL)
  533.          {
  534.             /* Funny line, we should issue a warning about it and just */
  535.             /* Ignore it for now                                       */
  536.          }
  537.          else
  538.          {
  539.             /* Parse out the name that is being assigned to */
  540.             val = p+1;
  541.  
  542.             /* Remove any trailing blanks from the name */
  543.             while(p > buf && p[-1] == ' ') p--;
  544.             *p = 0;
  545.             /* Skip over any leading blanks after the '=' */
  546.             while(*val == ' ') val++;
  547.             p = buf;
  548.  
  549.             /* Remove any leading blanks from the name */
  550.             while(*p == ' ') p++;
  551.             len = strlen(p);
  552.  
  553.             /* We now have p pointing to the nul terminated name and val pointing */
  554.             /* to a null terminated substitution string for the variable          */
  555.             if (len > 63)
  556.             {
  557.                /* The name is too long, we should issue a warning and truncate it */
  558.                p[63] = 0;
  559.             }
  560.             strcpy(name, p);
  561.             /* We might consider looking up the name to see if this is a redefinition */
  562.             /* For now we will be silent about it.                                */
  563.             Sym_Set(name, val, NULL);
  564.          }
  565.       }
  566.       cstate = 1;
  567.       if (extend) cstate = 2;
  568.    }
  569.    /* Now we also want to gather in the automatically generated stuff so that      */
  570.    /* we don't have to read it in again.  This would only need to be dealt with    */
  571.    /* If they actually change the type of project that we are building.  This will */
  572.    /* also have the effect of retaining any special configuration that they have   */
  573.    /* possibly created on another machine.                                         */
  574.    Sym_Set(SYM_HOLD, "", NULL);
  575.    while((len = fread(buf, 1, 255, fp)) > 0)
  576.    {
  577.       buf[len] = 0;
  578.       Sym_Set(SYM_HOLD, NULL, buf);
  579.    }
  580.    fclose(fp);
  581.  
  582.    go_dir(global.homedir);
  583.    UnLock(global.workdir);
  584.    global.workdir = xlockdir(Sym_Lookup("DIR"));
  585.    return(0);
  586. }
  587.  
  588. /***********************************************************************************
  589.  * Procedure: write_file
  590.  * Synopsis:  write_file()
  591.  * Purpose:   Write out a dmake file based on the current options
  592.  ***********************************************************************************/
  593. int write_file()
  594. {
  595.    FILE *fp;
  596.    char *place;
  597.    char *name, *val;
  598.    char *fname;
  599.  
  600.    go_dir(global.homedir);
  601.  
  602.    fname = Sym_Lookup(SYM_SCRIPT);
  603.  
  604.    name = Sym_Lookup("PROJECT");
  605.    if (!*name) name = "*"; /* Force an invalid project name */
  606.  
  607.    while(*name)
  608.    {
  609.       if (strchr("#?\\/*: []{}^$&|()\"'", *name))
  610.       {
  611.          request(1, TEXT_BADPROJ, Sym_Lookup("PROJECT"), NULL);
  612.          return(1);
  613.       }
  614.       name++;
  615.    }
  616.  
  617.    if(!*fname)
  618.    {
  619.       request(1, TEXT_NOPROJ, NULL , NULL);
  620.       return(1);
  621.    }
  622.  
  623.    while ((fp = fopen(fname, "w")) == NULL)
  624.    {
  625.       struct stat stat_buf;
  626.  
  627.       if ( (stat(fname, &stat_buf) >= 0) &&
  628.            (!(stat_buf.st_mode & S_IWRITE)))
  629.       {
  630.          /* The file exists, but is read-only.  See if they want us */
  631.          /* to check it out for them to work on                     */
  632.          if (!request(0, TEXT_SCRIPTCO, fname, NULL)) return;
  633.  
  634.          /* We need to check the file out for them */
  635.          exec_command(global.text[CONFIG_CO], fname);
  636.       }
  637.       else
  638.       {
  639.          request(0, TEXT_BADFILE, fname, NULL);
  640.          return(1);
  641.       }
  642.  
  643.    }
  644.  
  645.    place = NULL;
  646.    while(place = Sym_Next(place, &name, &val))
  647.    {
  648.       int nlen, vlen;
  649.  
  650.       nlen = strlen(name);
  651.       vlen = strlen(val);
  652.  
  653.       if (nlen + vlen < LINE_LEN)
  654.       {
  655.          if (fprintf(fp, "%s= %s\n", name, val) <= 0) goto ioerr;
  656.       }
  657.       else
  658.       {
  659.          if (fputs(name, fp) || fputs("= ", fp)) goto ioerr;
  660.          nlen += 2;
  661.          while(nlen + vlen > LINE_LEN)
  662.          {
  663.             /* Attempt to break the line at a space - DMAKE doesn't require   */
  664.             /* that the line end in a space, but it is easier for a person to */
  665.             /* edit it when it works out that way.                            */
  666.             nlen = LINE_LEN-nlen;
  667.             while((nlen > MIN_LINE) && val[nlen-1] != ' ') nlen--;
  668.  
  669.             /* If we couldn't find a space on the line, just break it at the */
  670.             /* target column anyways.                                        */
  671.             if (nlen == MIN_LINE) nlen = LINE_LEN-nlen;
  672.  
  673.             if (fwrite(val, nlen, 1, fp) != 1) goto ioerr;
  674.             if (fputs("\\\n", fp)) goto ioerr;
  675.             val += nlen;
  676.             vlen -= nlen;
  677.             nlen = 0;
  678.          }
  679.          if (fputs(val, fp) || (fputc('\n', fp) == EOF)) goto ioerr;
  680.       }
  681.    }
  682.    /* Add the separator line                                                */
  683.    if (fputs("\n#### AUTOMATICALLY GENERATED - DO NOT EDIT BELOW THIS LINE\n", fp))
  684.       goto ioerr;
  685.    if (fputs(Sym_Lookup(SYM_HOLD), fp)) goto ioerr;
  686.    fclose(fp);
  687.  
  688.    /* Now we need to create an icon for it */
  689.  
  690.    {
  691.       struct DiskObject *dobj = NULL;
  692.  
  693.       if (global.oldproject)
  694.          /* We read the project, so it may have a valid existing icon       */
  695.          dobj = GetDiskObject(fname);
  696.       if (dobj != NULL)
  697.       {
  698.          int len;
  699.          len = strlen(dobj->do_DefaultTool);
  700.          if ((len >= 5) && (!stricmp(dobj->do_DefaultTool+len-5, "vmake")))
  701.             ;
  702.          else
  703.          {
  704.             /* Don't trust it, make a new one                               */
  705.             FreeDiskObject(dobj);
  706.             dobj = NULL;
  707.          }
  708.       }
  709.       if (dobj == NULL)
  710.       {
  711.          dobj = GetDiskObject("DCC:Config/Default_Project");
  712.          if (dobj != NULL)
  713.             PutDiskObject(fname,dobj);
  714.       }
  715.       if (dobj != NULL)
  716.          FreeDiskObject(dobj);
  717.    }
  718.    return(0);
  719.  
  720. ioerr:
  721.    request(1, TEXT_IOERR, fname, NULL);
  722.    fclose(fp);
  723.    return(1);
  724. }
  725.  
  726.